﻿package src.computation {
	/**
	* 
	* Reliability project
	* -----------------------
	* @author  Dmitrij Boycov
	* @mail    dmitry.boycov@gmail.com
	* @date    April 27, 2010
	* @version 1
	* 
	*/
	
	
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;	
	import flash.events.Event;
	//
	import src.util.Container;
	import src.common.MsgEvent;
	import src.entity.Node;
	import src.entity.Element;
	import src.util.Utils;
	import src.computation.Method;
	
	public class LogicalProbabilisticMethod extends Method{
		
		private var rez:Number;		
		
		public function LogicalProbabilisticMethod(){			
		}				
			
		
		/*
		* 	+---+	    +---+         +---+ 
		*  -|   |-------|   |-   =>  -|   |-
		* 	+---+	    +---+         +---+
		*/	
		protected override function unitSeriesElement(idBetweenLink) {
			var betweenLink:CLink = this.links[idBetweenLink];			
			if (betweenLink.end != null && betweenLink.start != null && betweenLink.end["type"] == Element.TYPE && betweenLink.start["type"] == Element.TYPE) {
				var elOne:CElement = this.elements[betweenLink.start["id"]];
				var elTwo:CElement = this.elements[betweenLink.end["id"]];
				if (elOne != null && elTwo != null) {
					elOne.setTime(this.time);
					elTwo.setTime(this.time);
					var p = elOne.getProbability() * elTwo.getProbability();
					elOne.setProbability(p);			
					var idNewLink;
					if (betweenLink.id == elTwo.inLink) {
						idNewLink = elTwo.outLink;
					}
					if (betweenLink.id == elTwo.outLink) {
						idNewLink = elTwo.inLink;
					}					
					elOne.unLink(betweenLink.id);
					elTwo.unLink(betweenLink.id);
					delete this.links[betweenLink.id];
					elTwo.unLink(idNewLink);
					elOne.addLink(this.links[idNewLink], this.links);
					this.links[idNewLink].removeEnd(elTwo);
					this.links[idNewLink].addEnd(elOne);
					delete this.elements[elTwo.id];
				}
			}
		}
 	
		private function getParallEl(aEl:CElement):CElement {
			if (aEl == null) return null;
			if (!aEl.inLink || !aEl.outLink) return null;
			var lIn:CLink = this.links[aEl.inLink];
			var lOut:CLink = this.links[aEl.outLink];
			var oneNode:CNode = null;
			var twoNode:CNode = null;
			if (lIn.end["type"] == Node.TYPE) {
				oneNode = this.nodes[lIn.end["id"]];
			}
			if (lIn.start["type"] == Node.TYPE) {
				oneNode = this.nodes[lIn.start["id"]];
			}
			if (lOut.end["type"] == Node.TYPE) {
				twoNode = this.nodes[lOut.end["id"]];
			}
			if (lOut.start["type"] == Node.TYPE) {
				twoNode = this.nodes[lOut.start["id"]];
			}
			if (twoNode == null || oneNode == null) return null;
			var lIt;
			for each(lIt in oneNode.links) {
				var lIterator:CLink = this.links[lIt];
				if (lIn != lIterator && lOut != lIterator) {
					var elIterator:CElement = null;
					if (lIterator.start != null && lIterator.start["type"] == Element.TYPE) {
						elIterator = this.elements[lIterator.start["id"]];
					}
					if (lIterator.end != null && lIterator.end["type"] == Element.TYPE) {
						elIterator = this.elements[lIterator.end["id"]];
					}
					if (elIterator != null) {
						if (twoNode.links[elIterator.inLink] != null || twoNode.links[elIterator.outLink] != null) {
							return elIterator;
						}
					}
				}
			}
			return null;
		}	
		
		
		/**
		 *  transformTriangle 
		 * 									  0	u1
		 *			|						  |
		 * 	  +-----0-----+					+---+
		 * 	  |		u1	  |					|e1'|
		 * 	+---+		+---+				+---+  		
		 * 	|e1 |		|e3 |	=> 	 u2+---+  |	 +---+ u3	
		 * 	+---+		+---+		-0-|e2'|--0--|e3'|-0-	
		 * 	  |	  +---+	  |			   +---+	 +---+
		 * u2 0---|e2 |---0 u3		
		 * 	  |	  +---+	  |
		 */
		protected override function transformTriangle(e1:CElement, e2:CElement, e3:CElement, u1:CNode, u2:CNode, u3:CNode) {
			if (e1 == null || e2 == null || e3 == null || u1 == null || u2 == null || u3 == null) return;			
			var p1 = e1.getProbability();
			var p2 = e2.getProbability();
			var p3 = e3.getProbability();			
			// check devition by zero
			if (p1 == 0 && p2 == 0) return;
			var v1 = p1 + p2 * p3 - p1 * p2 * p3;
			var v2 = p3 + p1 * p2 - p1 * p2 * p3;
			var v3 = p2 + p1 * p3 - p1 * p2 * p3;			
			var px = Math.sqrt(v1 * v2 / v3);
			var py = v1 / px;
			var pz = v3 / py;
			e1.setProbability(px);
			e2.setProbability(py);
			e3.setProbability(pz);			
			//
			var newNode = this.addNode();
			var l:CLink;
			if (u2.haveSuchLink(e1.inLink)) {
				l = this.links[e1.inLink];
			}else if (u2.haveSuchLink(e1.outLink)) {
				l = this.links[e1.outLink];
			}
			if (l != null) {
				l.removeEnd(u2);
				newNode.addLink(l, this.links);
			}
			trace(4);//
			l = null;
			if (u3.haveSuchLink(e2.inLink)) {
				l = this.links[e2.inLink];
			}else if (u3.haveSuchLink(e2.outLink)) {
				l = this.links[e2.outLink];
			}
			if (l != null) {
				l.removeEnd(u3);
				newNode.addLink(l, this.links);
			}
			trace(5);//
			l = null;
			if (u1.haveSuchLink(e3.inLink)) {
				l = this.links[e3.inLink];
			}else if (u1.haveSuchLink(e3.outLink)) {
				l = this.links[e3.outLink];
			}
			if (l != null) {
				l.removeEnd(u1);
				newNode.addLink(l, this.links);
			}
			trace(6);
		}		
		
		private function reduceParallElements() {			
			trace("reduceParallElements()");
			var el:CElement;
			for each(el in this.elements) {
				var el2:CElement = this.getParallEl(el);
				if (el2 != null && el2 != el ) {
					// unit
					el2.setTime(this.time);
					el.setTime(this.time);
					var p = el2.getProbability() + el.getProbability()- el2.getProbability()*el.getProbability();
					el.setProbability(p);
					var inL:CLink = this.links[el2.inLink];
					var outL:CLink = this.links[el2.outLink];
					var n1 = inL.getOtherEnd(el2);
					if(n1["type"]==Node.TYPE){
						inL.removeEnd(this.nodes[n1["id"]]);
					}	
					var n2 = outL.getOtherEnd(el2);
					if (n2["type"] == Node.TYPE) {
						outL.removeEnd(this.nodes[n2["id"]]);
					}					
					inL.removeEnd(el2);
					outL.removeEnd(el2);
					delete this.links[inL.id];
					delete this.links[outL.id];
					delete this.elements[el2.id];
					//
					this.modified = true;
				}
			}
			this.printDict(this.elements);
		}
		
				
		public override function startComputation() {	
			if (this.nodes == null || this.elements == null || this.links == null) {				
				dispatchEvent(new MsgEvent(Method.ERROR, "Есть разрыв в схеме."));
				return false;
			}
			this.deleteFreeElements();
			var inNode = this.nodes[1];		
			if (Utils.sizeDict(inNode.links) > 0) {
				this.removeUnnecessaryNodes();	
				if (Utils.sizeDict(this.elements) == 1) {					
					dispatchEvent(new Event(Method.STOP_COMPUTATION));
					return true;
				}
				this.initElements();
				this.modified = true;
				this.printDict(this.elements);
				while (this.modified) {
					this.modified = false;				
					this.reduceTriangle();					
					this.reduceSeriesElemnts();
					this.reduceParallElements();
					this.removeUnnecessaryNodes();
				}
				dispatchEvent(new Event(Method.STOP_COMPUTATION));			
			}else { // inNode do not have link.				
				dispatchEvent(new MsgEvent(Method.ERROR,"Есть разрыв в схеме."));
			}
		}
		
		public override function getResult() {
			if (Utils.sizeDict(this.elements) == 1) {
				var el:CElement;
				for each(el in this.elements) {
					el.setTime(this.time);
					return "Вероятность безотказной работы:"+el.getProbability();
				}
			}
			return null; 
		}
		
	}
	
}